package modele;

import java.awt.Graphics2D;
import java.awt.Point;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import modele.case_.Case;
import modele.case_.CaseDrapeau;
import modele.case_.CaseTerre;
import modele.case_.CaseTrouDrapeau;
import modele.ex.ErreurFormatPlateau;
import modele.mode.Mode;
import outils.gestionnairefichier.ex.ErreurLecturePlateau;
import vue.Camera;

/**
 * Classe qui contient les données du plateau telles que ses cases et ses
 * dimensions
 *
 * @author twane
 */
public class Plateau implements Dessinable {

	private enum SeparationPlateau {

		HORIZONTAL,
		VERTICAL,
		DIAGONALE;

		private static SeparationPlateau getSeparationPlateauChar(char c) throws ErreurFormatPlateau {
			switch (c) {
				case 'h':
					return HORIZONTAL;

				case 'v':
					return VERTICAL;

				case 'd':
					return DIAGONALE;

				default:
					throw new ErreurFormatPlateau();
			}
		}
	}
	private int largeur;
	private int hauteur;
	private SeparationPlateau separationPlateau;
	private Case[][] cases;
	private ArrayList<Drapeau> drapeaux;
	private Mode mode;
	/**
	 * La taille d'une case en pixel
	 */
	public static final int TAILLE_CASE = 50;

	/**
	 * Crée un plateau en fonction du fichier présent au chemin passé en
	 * paramètre
	 *
	 * @param chemin l'adresse du fichier contenant le plateau
	 */
	private Plateau() {
		drapeaux = new ArrayList<Drapeau>();
	}

	/**
	 *
	 * @return les cases du plateau
	 */
	public Case[][] getCases() {
		return cases;
	}

	/**
	 *
	 * @return le mode de jeu courant
	 */
	public Mode getMode() {
		return mode;
	}

	/**
	 * Défini le mode de jeu courant
	 *
	 * @param mode
	 */
	public void setMode(Mode mode) {
		this.mode = mode;
		mode.mettreAJour(this);
	}

	/**
	 *
	 * @param x l'abscisse de la case
	 * @param y l'ordonnée de la case
	 * @return la case présente aux coordonnées passées en paramtère
	 */
	public Case getCase(int x, int y) {
		if (x >= 0 && x < cases.length && y >= 0 && y < cases[x].length) {
			return cases[x][y];
		} else {
			return null;
		}
	}

	/**
	 *
	 * @param p les coordonnées de la case requise
	 * @return la case présente aux coordonnées passées en paramètre
	 */
	public Case getCase(Point p) {
		return getCase(p.x, p.y);
	}

	/**
	 *
	 * @return tous les drapeaux
	 */
	public ArrayList<Drapeau> getDrapeaux() {
		return drapeaux;
	}

	/**
	 * Supprime les drapeaux de la carte pour le mode "à mort"
	 */
	public void supprimerDrapeaux() {
		for (Drapeau d : drapeaux) {
			d.getCase().setDrapeau(null);
		}
		drapeaux.clear();
	}

	/**
	 * Supprime les cases contenant les drapeaux pour le mode sans drapeaux et
	 * les remplace par des cases terre
	 */
	public void supprimerCasesDrapeaux() {
		for (int x = 0; x < largeur; x++) {
			for (int y = 0; y < hauteur; y++) {
				Case c = cases[x][y];
				if (c instanceof CaseTrouDrapeau || c instanceof CaseDrapeau) {
					cases[x][y] = new CaseTerre(x, y);
				}
			}
		}
	}

	/**
	 * Crée chaque case en fonction du fichier passé en paramètre
	 *
	 * @param chemin adresse du fichier contenant le plateau
	 * @return le nouveau plateau créé
	 * @throws ErreurLecturePlateau
	 * @throws ErreurFormatPlateau
	 */
	public static Plateau lirePlateau(String chemin) throws ErreurLecturePlateau, ErreurFormatPlateau {
		Plateau p = new Plateau();
		try {
			BufferedReader br = new BufferedReader(new FileReader(chemin));

			String ligne;
			String premiereLigne[];
			if ((ligne = br.readLine()) != null) {
				premiereLigne = ligne.split(":");
				p.largeur = Integer.parseInt(premiereLigne[0]);
				p.hauteur = Integer.parseInt(premiereLigne[1]);
				p.separationPlateau = SeparationPlateau.getSeparationPlateauChar(premiereLigne[2].charAt(0));
				p.cases = new Case[p.largeur][p.hauteur];
			} else {
				throw new ErreurFormatPlateau();
			}

			assert p.largeur >= 5 && p.hauteur >= 5 : "La carte est trop petite";

			for (int y = 0; y < p.hauteur; y++) {
				if ((ligne = br.readLine()) != null && ligne.length() >= p.largeur) {
					for (int x = 0; x < p.largeur; x++) {
						Case c = Case.getCaseCode(ligne.charAt(x), x, y);
						p.cases[x][y] = c;
						if (c.getDrapeau() != null) {
							p.drapeaux.add(c.getDrapeau());
						}
					}
				} else {
					throw new ErreurFormatPlateau();
				}
			}

		} catch (IOException e) {
			throw new ErreurLecturePlateau(chemin);
		}
		return p;
	}

	/**
	 *
	 * @return la largeur du plateau
	 */
	public int getLargeur() {
		return largeur;
	}

	/**
	 *
	 * @return la hauteur du plateau
	 */
	public int getHauteur() {
		return hauteur;
	}

	/**
	 *
	 * @param pos les coordonnées où l'on souhaite placer le pion
	 * @param joueur le joueur désirant placer le pion
	 * @return vrai si on peut placer un pion du joueur passé en paramètre
	 */
	public boolean isPositionnable(Point pos, Joueur joueur) {
		int numJoueur = joueur.getNumero();
		switch (separationPlateau) {
			case HORIZONTAL:
				if (numJoueur == 1) {
					return pos.y < (hauteur / 3);
				} else {
					return pos.y >= hauteur - (hauteur / 3);
				}
			case VERTICAL:
				if (numJoueur == 1) {
					return pos.x < (largeur / 3);
				} else {
					return pos.x >= largeur - (largeur / 3);
				}
			case DIAGONALE:
				if (numJoueur == 1) {
					return pos.x + pos.y < (Math.max(largeur, hauteur) * 2 / 3);
				} else {
					return (largeur - 1 - pos.x) + (hauteur - 1 - pos.y) < (Math.max(largeur, hauteur) * 2 / 3);
				}
		}
		return false;
	}

	/**
	 * Choisi automatiquement des pions pour le joueur
	 *
	 * @param joueur le joueur qui va se voir affecter les pions
	 */
	public void placementAutomatiquePions(Joueur joueur) {
		ArrayList<Case> casesAccessibles = new ArrayList<Case>();
		for (int x = 0; x < largeur; x++) {
			for (int y = 0; y < hauteur; y++) {
				if (isPositionnable(new Point(x, y), joueur)) {
					casesAccessibles.add(getCase(x, y));
				}
			}
		}
		joueur.placementAutomatiquePions(casesAccessibles);
	}

	@Override
	public void dessiner(Graphics2D gr, Camera camera) {
		for (Case[] c1 : cases) {
			for (Case c2 : c1) {
				c2.dessiner(gr, camera);
			}
		}
	}

	/**
	 *
	 * @return une copie du plateau
	 */
	public Plateau copier() {
		Plateau p = new Plateau();
		p.largeur = largeur;
		p.hauteur = hauteur;
		p.cases = new Case[largeur][hauteur];
		p.separationPlateau = separationPlateau;
		for (int x = 0; x < largeur; x++) {
			for (int y = 0; y < hauteur; y++) {
				p.cases[x][y] = cases[x][y].copier();
			}
		}
		for (Drapeau d : drapeaux) {
			Drapeau copie = d.copier();
			p.drapeaux.add(copie);
			int x = d.getCase().getX();
			int y = d.getCase().getY();
			p.cases[x][y].setDrapeau(copie);
		}
		return p;
	}
}
